package com.platform.framework.serviceimpl.wxpay;


import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.annotation.Resource;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.alipay.demo.trade.utils.Utils;
import com.platform.framework.dao.pub.TradeFlowMybatisDao;
import com.platform.framework.entity.pub.TradeFlow;
import com.platform.framework.entity.wxpay.WxpayReturnInfo;
import com.platform.framework.entity.wxpay.WxRefundReturnInfo;
import com.platform.framework.gateway.wxpay.config.WxpayConfig;
import com.platform.framework.gateway.wxpay.inf.WxpayGateway;
import com.platform.framework.serviceimpl.alipay.AlipayServiceImpl;
import com.platform.framework.serviceinf.pub.TradeFlowService;
import com.platform.framework.serviceinf.wxpay.WxRefundReturnInfoService;
import com.platform.framework.serviceinf.wxpay.WxpayReturnInfoService;
import com.platform.framework.serviceinf.wxpay.WxpayService;
import com.platform.framework.util.pub.ReflectUtil;
import com.platform.framework.util.pub.Validation;
import com.platform.framework.util.pub.XmlUtil;
import com.platform.framework.util.sign.SignUtil;
import com.platform.framework.vo.exception.ReceiverException;
import com.platform.framework.vo.info.ExceptionInfo;
import com.platform.framework.vo.pub.ResponseVo;
import com.platform.framework.vo.states.PayResult;
import com.platform.framework.vo.transfer.TransportLayer;
/**
 * WxpayService实现类
 * @author ly-liuxianliang
 *
 */
@Service("wxpayService")
public class WxpayServiceImpl implements WxpayService {
	
	private static Logger logger = Logger.getLogger(AlipayServiceImpl.class);
	
	@Resource
	private TradeFlowMybatisDao tradeFlowMybatisDao;
	
	@Resource
	private WxRefundReturnInfoService wxRefundReturnInfoService;
	
	@Resource
	private WxpayGateway wxpayGateway;
	
	@Resource
	private WxpayReturnInfoService wxpayReturnInfoService;
	
	@Resource
	private TradeFlowService tradeFlowService;
	
	@Override
	public ResponseVo wxH5Pay(TransportLayer transportLayer) throws Exception {
		// 微信公众号支付
		// 获取预下单id  prepay_id
		TransportLayer transportLayer2 = wxpayGateway.unifiedOrder(transportLayer);
		transportLayer2.setAppId(transportLayer.getAppId());
		transportLayer2.setWxKey(transportLayer.getWxKey());
		String jsCode = getJsCode(transportLayer2);
		ResponseVo responseVo = new ResponseVo();
		responseVo.setReturnMsg(transportLayer2.getReturnMsg());
//		responseVo.setJsCode(transportLayer2.getPrepayId());
		responseVo.setJsCode(jsCode);
		responseVo.setTradeFlowId(Integer.parseInt(transportLayer.getTradeFlowId()));
		logger.info("prepay_id is "+transportLayer2.getPrepayId());
		return responseVo;
	}

	private static String getJsCode(TransportLayer transportLayer) {
		logger.info("-------------- getJsApi String start --------------");
		// test
		if(transportLayer.getAppId() == null){
			transportLayer.setAppId("wx950ef94f07fd3be1");
		}
		logger.info("appid in vo is "+transportLayer.getAppId());

		String nonce_str = UUID.randomUUID().toString().replaceAll("-", "");
		long timeStamp = System.currentTimeMillis()/1000;
		Map<String,Object> map = new HashMap<>();
		map.put("appId", transportLayer.getAppId());
		map.put("timeStamp", timeStamp);
		map.put("nonceStr", nonce_str);
		map.put("package", "prepay_id="+transportLayer.getPrepayId());
		map.put("signType", "MD5");
		String jsapi = "function onBridgeReady(){"+
				  " WeixinJSBridge.invoke("+
				       "'getBrandWCPayRequest', {"+
				           "\"appId\":\""+transportLayer.getAppId()+"\", "+    //公众号名称，由商户传入     
				           "\"timeStamp\":\""+timeStamp+"\", "+        //时间戳，自1970年以来的秒数     
				           "\"nonceStr\":\""+nonce_str+"\","+ //随机串     
				           "\"package\":\"prepay_id="+transportLayer.getPrepayId()+"\", "+    
				           "\"signType\":\"MD5\",  "+       //微信签名方式：     
				           "\"paySign\": \""+SignUtil.getSign(map,transportLayer)+"\" "+//微信签名 
				       "},"+
				       "function(res){  "+   
				           "if(res.err_msg == \"get_brand_wcpay_request:ok\" ) {eval(\""+transportLayer.getSuccessJs()+"\");} else {eval(\""+transportLayer.getFailJs()+"\");}  "+   // 使用以上方式判断前端返回,微信团队郑重提示：res.err_msg将在用户支付成功后返回    ok，但并不保证它绝对可靠。 
				       "}"+
				   "); "+
				"}"+
				   
				"function getTradeFlowId() {" +
					" return " + transportLayer.getTradeFlowId() +
				"}" +	
				
				"if (typeof WeixinJSBridge == \"undefined\"){"+
				   "if( document.addEventListener ){"+
				       "document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);"+
				   "}else if (document.attachEvent){"+
				       "document.attachEvent('WeixinJSBridgeReady', onBridgeReady);"+ 
				       "document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);"+
				   "}"+
				"}else{"+
				   "onBridgeReady();"+
				"}";
		logger.info("wxpaylog:jsapi="+jsapi);
		return jsapi;
	}

	@Override
	@Transactional(readOnly = false, propagation=Propagation.REQUIRED)
	public ResponseVo wxBarCodePay(TransportLayer transportLayer)
			throws Exception {
		try {
			TradeFlow tradeFlow = tradeFlowMybatisDao.getById(Integer.parseInt(transportLayer.getTradeFlowId()));
			WxpayReturnInfo wxpayReturnInfo = wxpayGateway.wxpayBarCode(transportLayer);
			
			String reverseResult = "";
			String resultCode = "";
			if(wxpayReturnInfo.getResultCode().equals("SUCCESS")){
				tradeFlow.setPayResult(PayResult.TRADE_SUCCESS);  //交易成功时将交易结果保存在流水表中
				tradeFlow.setOpenId(wxpayReturnInfo.getOpenid());
				wxpayReturnInfo.setTradeFlowId(tradeFlow.getId());
				wxpayReturnInfoService.saveOrUpdate(wxpayReturnInfo);
			}else{
				Thread.sleep(5000);   //交易出现系统错误时则等待5秒后调用查询API进行查单
				WxpayReturnInfo queryWxpayReturnInfo = wxpayGateway.tradeQuery(transportLayer);
				//当查询结果为USERPAYING时采用轮询操作
				if("SUCCESS".equals(queryWxpayReturnInfo.getTradeState())){
					tradeFlow.setPayResult(PayResult.TRADE_SUCCESS);
					tradeFlow.setOpenId(queryWxpayReturnInfo.getOpenid());
					queryWxpayReturnInfo.setTradeFlowId(tradeFlow.getId());
					wxpayReturnInfoService.saveOrUpdate(queryWxpayReturnInfo);
				}
				else if(queryWxpayReturnInfo.getTradeState() != null && queryWxpayReturnInfo.getTradeState().equals("USERPAYING")){ 
					Boolean isTimeout = false;
					 for (int i = 1; i <= WxpayConfig.MaxQueryRetry; i++) {
				            Utils.sleep(WxpayConfig.QueryDuration);
				            logger.info("--^_^--正在进行第"+i+"次轮询,请耐心等待--^_^--");
				            WxpayReturnInfo queryWxpayReturnInfo2 = wxpayGateway.tradeQuery(transportLayer);
				            if(queryWxpayReturnInfo2.getTradeState() != null){
				            	if(stopQuery(queryWxpayReturnInfo2.getTradeState())){
				            		tradeFlow.setPayResult(PayResult.TRADE_SUCCESS);
				            		tradeFlow.setOpenId(queryWxpayReturnInfo2.getOpenid());
				            		queryWxpayReturnInfo2.setTradeFlowId(tradeFlow.getId());
				            		wxpayReturnInfoService.saveOrUpdate(queryWxpayReturnInfo2);
				            		break;
				            	}
				            }
				            if(i == WxpayConfig.MaxQueryRetry) {
				            	isTimeout = true;
				            }
					 }	
					 if(isTimeout){
						//轮询超时调用撤销接口
						reverseResult = wxpayGateway.wxReverse(transportLayer);
						resultCode = XmlUtil.getParam(reverseResult, "result_code");
						if("SUCCESS".equals(resultCode)){
							logger.info("--^_^--轮询超时,调用撤销接口撤单成功--^_^--");
							tradeFlow.setPayResult(PayResult.REVOKED);
							queryWxpayReturnInfo.setTradeState(PayResult.REVOKED);
							queryWxpayReturnInfo.setTradeFlowId(tradeFlow.getId());
							wxpayReturnInfoService.saveOrUpdate(queryWxpayReturnInfo);
						}
					 }
				}else if(queryWxpayReturnInfo.getTradeState() == null ){  //如果交易状况不明晰，则调用撤销接口
					 reverseResult = wxpayGateway.wxReverse(transportLayer);
					 resultCode = XmlUtil.getParam(reverseResult, "result_code");
					if("SUCCESS".equals(resultCode)){
						logger.info("--^_^--交易状况不明晰,调用撤销接口撤单成功--^_^--");
						tradeFlow.setPayResult(PayResult.REVOKED);
						queryWxpayReturnInfo.setTradeState(PayResult.REVOKED);
						queryWxpayReturnInfo.setTradeFlowId(tradeFlow.getId());
						wxpayReturnInfoService.saveOrUpdate(queryWxpayReturnInfo);
					}
				}else{
					tradeFlow.setPayResult(PayResult.TRADE_FAIL);
				}
			}
			
			tradeFlowService.saveOrUpdate(tradeFlow);
			return new ResponseVo(tradeFlow.getPayResult(), wxpayReturnInfo.getResultCode(), wxpayReturnInfo.getReturnMsg(), tradeFlow.getId()); // 返回交易流水号
		} catch (ReceiverException e1) {
			logger.error("WxpayServiceImpl-->wxpayBarcode() Method Error,exception is : seller id is null.");
			return new ResponseVo(PayResult.ERROR, null, ExceptionInfo.SELLER_ID_EXCEPTION,  null);
		} catch (Exception e2){
			logger.error("WxpayServiceImpl-->wxpayBarcode() Method Error,exception is :"+ e2);
			e2.printStackTrace();
		}
		return null;
		
	}

	@Override
	public ResponseVo wxQRCodePay(TransportLayer transportLayer)
			throws Exception {
		// TODO Auto-generated method stub
		TransportLayer transportLayer2 = wxpayGateway.unifiedOrder(transportLayer);
		ResponseVo responseVo = new ResponseVo();
		responseVo.setCodeUrl(transportLayer2.getCodeUrl());
		responseVo.setReturnMsg(transportLayer2.getReturnMsg());
		if(Validation.isEmpty(transportLayer2.getCodeUrl())){
			return responseVo;
		}
		return responseVo;
	}

	@Override
	public ResponseVo wxQueryTradeResult(TransportLayer transportLayer)
			throws Exception {
		// TODO Auto-generated method stub
		TradeFlow tradeFlow = ReflectUtil.voPersistence(new TradeFlow(), transportLayer);
		if (!Validation.isEmpty(transportLayer.getTradeFlowId())) {
			tradeFlow.setId(Integer.parseInt(transportLayer.getTradeFlowId()));
		}
		List<TradeFlow> list = tradeFlowMybatisDao.findTradeFlow(tradeFlow);
		if(Validation.isEmptyList(list)){
			return null;
		}
		List<TradeFlow> resultList = new ArrayList<TradeFlow>();
		for (TradeFlow tradeFlow2 : list) {
			if (!Validation.isEmpty(tradeFlow2.getPayResult())) {
				resultList.add(tradeFlow2);
			}
		}
		return new ResponseVo(PayResult.TRADE_SUCCESS, null, "querysuccess", null, resultList);
	}

	@Override
	public ResponseVo wxRefund(TransportLayer transportLayer) throws Exception {
		// TODO Auto-generated method stub
		try {
			String resultXml = wxpayGateway.doRefund(transportLayer);
			String resultCode = XmlUtil.getParam(resultXml, "result_code");
			String returnCode = XmlUtil.getParam(resultXml, "return_code");
			logger.info("resultXml = " + resultXml);
			if("FAIL".equals(resultCode) || "FAIL".equals(returnCode) ) {
				return saveReturnInfoAndSetPayResult(transportLayer, resultXml, PayResult.REFUND_FAIL);
			} else if ("SUCCESS".equals(resultCode)) {
				return saveReturnInfoAndSetPayResult(transportLayer, resultXml, PayResult.REFUND_SUCCESS);
			} 
			
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
	
	private ResponseVo saveReturnInfoAndSetPayResult(TransportLayer transportLayer, String resultXml,
			String payResult) {
		// TODO Auto-generated method stub
		WxRefundReturnInfo returnInfo = new WxRefundReturnInfo();
		returnInfo.setTradeFlowId(Integer.parseInt(transportLayer.getTradeFlowId()));
		returnInfo.setCreator(transportLayer.getLoginName());
		returnInfo.setCreateDate(new Date());
		XmlUtil.xml2Entity(resultXml, returnInfo);
		logger.info("微信退款返回信息:returnInfo="+returnInfo);
		if(!Validation.isEmpty(returnInfo.getTotalFee())){
			Double totalFee = Double.parseDouble(returnInfo.getTotalFee())*0.01;
			returnInfo.setTotalFee(totalFee.toString());
		}
		if(!Validation.isEmpty(returnInfo.getCashFee())){
			Double cashFee = Double.parseDouble(returnInfo.getCashFee())*0.01;
			returnInfo.setCashFee(cashFee.toString());
		}
		if(!Validation.isEmpty(returnInfo.getRefundFee())){
			Double refundFee = Double.parseDouble(returnInfo.getRefundFee())*0.01;
			returnInfo.setTotalFee(refundFee.toString());
		}
		wxRefundReturnInfoService.saveOrUpdate(returnInfo);
		TradeFlow tradeFlow = tradeFlowMybatisDao.getById(Integer.parseInt(transportLayer.getTradeFlowId()));
		tradeFlow.setPayResult(payResult);
		tradeFlowService.saveOrUpdate(tradeFlow);
		return new ResponseVo(payResult, returnInfo.getReturnCode(), returnInfo.getReturnMsg(), tradeFlow.getId()); // 返回交易流水号
	}

	// 判断是否停止查询
    protected boolean stopQuery(String queryResult) {
        if ("SUCCESS".equals(queryResult)) {
            // 如果查询到交易成功，则返回对应结果
            return true;
        }else{
        	return false;
        }
    }

}
